﻿using System;
using System.Collections.Generic;
using System.Xml;
using System.IO;

namespace VidoopSecure
{
    public class Category
    {
        private string _id;
        private string _uri;
        private string _name;
        private List<CategoryImage> _images;
        private string _xml;

        /// <summary>
        /// Category Id.
        /// </summary>
        public string Id
        {
            get { return _id; }
            set { _id = value; }
        }

        /// <summary>
        /// Canonical URI for this Category.
        /// </summary>
        public string Uri
        {
            get { return _uri; }
            set { _uri = value; }
        }

        /// <summary>
        /// Category name.
        /// </summary>
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        /// <summary>
        /// Array of representative images for this Category.
        /// </summary>
        public List<CategoryImage> Images
        {
            get { return _images; }
            set { _images = value; }
        }

        /// <summary>
        /// Raw XML string from which this Category was created.`
        /// </summary>
        public string Xml
        {
            get { return _xml; }
            set { _xml = value; }
        }
        
        /// <summary>
        /// Determines whether <paramref name="obj"/> is equal to the 
        /// current object.
        /// </summary>
        /// <param name="obj">The <see cref="System.Object" /> to compare against.</param>
        /// <returns>true if the specified object is equal to the current object.</returns>
        public override bool  Equals(object obj)
        {
            Category other = obj as Category;
            if (other == null)
                return false;

            return (other.Id == this.Id &&
                    other.Name == this.Name &&
                    other.Uri == this.Uri &&
                    other.Xml == this.Xml &&
                    Category.ImagesAreEqual(other.Images, this.Images));
        }

        /// <summary>
        /// Serves as a hashcode for the type.
        /// </summary>
        /// <returns>An integer representing a unique hashcode for the object.</returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Determines whether <paramref name="list1"/> and 
        /// <paramref name="list2"/> contain identical lists of 
        /// <see cref="VidoopSecure.CategoryImage" /> objects.
        /// </summary>
        /// <param name="list1">A <see cref="System.Collections.Generic.List" />
        /// of <see cref="VidoopSecure.CategoryImage" /> objects.</param>
        /// <param name="list2">A <see cref="System.Collections.Generic.List" />
        /// of <see cref="VidoopSecure.CategoryImage" /> objects.</param>
        /// <returns>true if the <see cref="VidoopSecure.CategoryImage" />
        /// objects in the lists are the same.</returns>
        static public bool ImagesAreEqual(List<CategoryImage> list1,
                                          List<CategoryImage> list2)
        {
            if (list1 == null && list2 == null)
                return true;
            else if (list1 == null || list2 == null)
                return false;

            if (list1.Count != list2.Count)
                return false;

            foreach (CategoryImage ci1 in list1)
            {
                if (!list2.Contains(ci1))
                    return false;
            }

            return true;
        }

        /// <summary>
        /// Generate a Category instance from <paramref name="xml" />.
        /// </summary>
        /// <param name="xml">A XML string representing a
        /// <see cref="VidoopSecure.Category" />.</param>
        /// <returns>An instance of a <see cref="VidoopSecure.Category" />
        /// object.</returns>
        static public Category FromXml(string xml)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);
            return GenerateCategoryFromXmlDocument(doc);
        }


        /// <summary>
        /// Generate a Category instance from the XML in 
        /// <paramref name="stream" />.
        /// </summary>
        /// <param name="stream">An <see cref="System.IO.Stream" /> containing
        /// XML that represents a <see cref="VidoopSecure.Category" />.</param>
        /// <returns>An instance of a <see cref="VidoopSecure.Category" />
        /// object.</returns>
        static public Category FromXml(Stream stream)
        {

            XmlDocument doc = new XmlDocument();
            doc.Load(stream);
            return GenerateCategoryFromXmlDocument(doc);
        }


        /// <summary>
        /// Generate a list of Category objects from <paramref name="xml"/>.
        /// </summary>
        /// <param name="xml">An XML string representing a collection of 
        /// Category objects.</param>
        /// <returns>A <see cref="System.Collections.Generic.List" />
        /// of <see cref="VidoopSecure.Category" /> objects. If 
        /// no Categories found in the XML, an empty 
        /// <see cref="System.Collections.Generic.List" /> will be
        /// returned.</returns>
        static public List<Category> FromCategoriesXml(string xml)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);
            return GenerateCategoryListFromXmlDocument(doc);
        }

        /// <summary>
        /// Generate a list of Category objects from the XML in 
        /// <paramref name="stream"/>.
        /// </summary>
        /// <param name="stream">An <see cref="System.IO.Stream" /> containing
        /// XML that represents a collection of 
        /// <see cref="VidoopSecure.Category" /> objects.</param>
        /// <returns>A <see cref="System.Collections.Generic.List" />
        /// of <see cref="VidoopSecure.Category" /> objects. If 
        /// no Categories found in the XML, an empty 
        /// <see cref="System.Collections.Generic.List" /> will be
        /// returned.</returns>
        static public List<Category> FromCategoriesXml(Stream stream)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(stream);
            return GenerateCategoryListFromXmlDocument(doc);
        }

        static private List<Category> GenerateCategoryListFromXmlDocument(XmlDocument doc)
        {
            List<Category> categories = new List<Category>();

            XmlNodeList categoryNodes = doc.GetElementsByTagName("category");
            if (categoryNodes != null && categoryNodes.Count > 0)
            {
                foreach(XmlNode node in categoryNodes)
                {
                    Category cat = Category.FromXml(node.OuterXml);
                    if (cat != null)
                        categories.Add(cat);
                }
            }
            
            return categories;
        }

        static private Category GenerateCategoryFromXmlDocument(XmlDocument doc)
        {
            Category category = new Category();

            category.Id = doc.GetElementsByTagName("id")[0].InnerText;
            category.Name = doc.GetElementsByTagName("name")[0].InnerText;

            XmlNodeList imageNodes = doc.GetElementsByTagName("imageURI");
            category.Images = new List<CategoryImage>(imageNodes.Count);
            foreach(XmlNode node in imageNodes)
                category.Images.Add(CategoryImage.FromXml(node.OuterXml));

            return category;
        }
    }
}
